//
// Created by uts on 10/28/21.
//

#include "AppShareMemory.h"
#include <exception>
/*
* Function:The constructor of ShareMemory Class
* params：key，assigned key；size: size；flag: the flag of the share memory
* return value: None
*/
using namespace std;

AppShareMemory::AppShareMemory(const char *name,int size,int block_size)
{
    this->name = name;
    this->block_size = block_size;
    this->m_size = size;
    this->total_size = this->block_size*this->m_size;
//    this->share_memory_fd = shm_open(this->name,O_CREAT|O_RDWR,0644);
    this->share_memory_fd = shm_open(this->name,O_RDWR,0644);
    if(this->share_memory_fd < 0){
        cerr<<"Open share memory "<<this->name<<" failed"<<endl;
        this->error_number = OPEN_MEM_ERROR;
        return;
    }
    off_t mem_size = this->total_size;
    ftruncate(this->share_memory_fd,mem_size);
    this->initial_mem_pointer = (unsigned char *)mmap(NULL,
                                                      mem_size,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_NORESERVE,
                                                      this->share_memory_fd,0);
    this->mem_pointer = this->initial_mem_pointer;
    memset(this->mem_pointer,0,mem_size);
    if (MAP_FAILED == this->mem_pointer){
        cerr<<"Mapping share memory "<<this->name<<" failed"<<endl;
        this->error_number = MAP_MEM_ERROR;
    }
}
/*
* function: the destructor of ShareMemory Class;
* params: None
* return value: None
*/
AppShareMemory::~AppShareMemory()
{
    munmap(this->mem_pointer,this->total_size);
    close(this->share_memory_fd);
    shm_unlink(this->name);
}



bool AppShareMemory::push(DataBlock *block) {
    try{
        int current_cursor = this->cursor;
        this->mem_pointer = this->initial_mem_pointer+this->block_size*this->cursor;
        unsigned char read_flag = 0;
        if(this->check_overflow){
            read_flag = *(this->mem_pointer+8);
            while (read_flag ==1){
                this->cursor ++;
                this->cursor %= this->m_size;
                if (this->cursor == current_cursor){
                    clog<<this->name<<" share memory overflowed!"<<endl;
                    break;
                }
                this->mem_pointer = this->initial_mem_pointer+this->block_size*this->cursor;
                read_flag = *(this->mem_pointer+8);
            }
        }
        memcpy(this->mem_pointer,&block->channel,sizeof(block->channel));
        int offset = sizeof(block->channel);
        memcpy(this->mem_pointer+offset,&block->flag,sizeof(block->flag));
        offset += sizeof(block->flag);
        memcpy(this->mem_pointer+offset,&block->length,sizeof(block->length));
        offset += sizeof(block->length);
        memcpy(this->mem_pointer+offset,&block->frame_serial_no,sizeof(block->frame_serial_no));
        offset += sizeof(block->frame_serial_no);
        memcpy(this->mem_pointer+offset,&block->supplement_position,sizeof(block->supplement_position));
        offset += sizeof(block->supplement_position);
        memcpy(this->mem_pointer+offset,&block->timestamp,sizeof(block->timestamp));
        offset += sizeof(block->timestamp);
        memcpy(this->mem_pointer+offset,&block->width,sizeof(block->width));
        offset += sizeof(block->width);
        memcpy(this->mem_pointer+offset,&block->height,sizeof(block->height));
        offset += sizeof(block->height);
//        memcpy(this->mem_pointer+offset,&block.detected_count,sizeof(block.detected_count));
//        offset += sizeof(block.detected_count);
//        if(block.detected_count>0){
//            int detect_size = sizeof(DetectResult)*block.detected_count;
//            memcpy( this->mem_pointer+offset,block.results,detect_size);
//            offset += detect_size;
//        }
        if(block->data_length){
            memcpy(this->mem_pointer+offset,block->data,block->data_length);
        }else{
            memcpy(this->mem_pointer+offset,block->data,block->width*block->height*3);
        }
        this->cursor++;
        this->cursor %= this->m_size;
        //delete block.results;
        delete block->data;
        delete block;
        return true;
    }catch(exception &e){
        return false;
    }
}

bool AppShareMemory::push(unsigned int flag,
                          unsigned long channel,
                          int length,int frame_serial_no,
                          int supplement_position,unsigned long timestamp,
                          int width,int height,int data_length,unsigned char *data){
    try{
        int current_cursor = this->cursor;
        this->mem_pointer = this->initial_mem_pointer+this->block_size*this->cursor;
        unsigned char read_flag = 0;
        if(this->check_overflow){
            read_flag = *(this->mem_pointer+8);
            while (read_flag ==1){
                this->cursor ++;
                this->cursor %= this->m_size;
                if (this->cursor == current_cursor){
                    clog<<this->name<<" share memory overflowed!"<<endl;
                    break;
                }
                this->mem_pointer = this->initial_mem_pointer+this->block_size*this->cursor;
                read_flag = *(this->mem_pointer+8);
            }
        }
        memcpy(this->mem_pointer,&channel,sizeof(unsigned long));
        int offset = sizeof(unsigned long);
        memcpy(this->mem_pointer+offset,&flag,sizeof(unsigned int));
        offset += sizeof(unsigned int);
        memcpy(this->mem_pointer+offset,&length,sizeof(int));
        offset += sizeof(int);
        memcpy(this->mem_pointer+offset,&frame_serial_no,sizeof(int));
        offset += sizeof(int);
        memcpy(this->mem_pointer+offset,&supplement_position,sizeof(int));
        offset += sizeof(int);
        memcpy(this->mem_pointer+offset,&timestamp,sizeof(unsigned long));
        offset += sizeof(unsigned long);
        memcpy(this->mem_pointer+offset,&width,sizeof(int));
        offset += sizeof(int);
        memcpy(this->mem_pointer+offset,&height,sizeof(int));
        offset += sizeof(int);
//        memcpy(this->mem_pointer+offset,&block.detected_count,sizeof(block.detected_count));
//        offset += sizeof(block.detected_count);
//        if(block.detected_count>0){
//            int detect_size = sizeof(DetectResult)*block.detected_count;
//            memcpy( this->mem_pointer+offset,block.results,detect_size);
//            offset += detect_size;
//        }
//        memset(this->mem_pointer+offset,0,data_length);
        memcpy(this->mem_pointer+offset,data,data_length);
        this->cursor++;
        this->cursor %= this->m_size;
        //delete block.results;
        return true;
    }catch(exception &e){
        return false;
    }
}


bool AppShareMemory::pull(DataBlock *block) {
    try{
        this->mem_pointer = this->initial_mem_pointer+this->block_size*this->cursor;
        unsigned int read_flag = 0;
        read_flag = *(this->mem_pointer+8);
        if (read_flag !=0){
            unsigned int self_read_flag = read_flag & 2147483648;
            if(!self_read_flag){
                block->channel = *(this->mem_pointer);
                int offset = sizeof(block->channel);
                block->flag = read_flag;
                offset += sizeof(block->flag);
                block->length = *(this->mem_pointer+offset);
                offset += sizeof(block->length);
                block->frame_serial_no = *(this->mem_pointer+offset);
                offset += sizeof(block->frame_serial_no);
                block->supplement_position = *(this->mem_pointer+offset);
                offset += sizeof(block->supplement_position);
                block->timestamp = *(this->mem_pointer+offset);
                offset += sizeof(block->timestamp);
                block->width = *(this->mem_pointer+offset);
                offset += sizeof(block->width);
                block->height = *(this->mem_pointer+offset);
                offset += sizeof(block->height);
                block->data_length = block->length - sizeof(block->frame_serial_no)
                                     -sizeof(block->timestamp)
                                     -sizeof(block->width)
                                     -sizeof(block->height)
                                     -sizeof(block->supplement_position);
                block->data = new unsigned char[block->data_length];
                memcpy(block->data,this->mem_pointer+offset,block->data_length);
                this->cursor++;
                this->cursor %= this->m_size;
            }
        }
        return true;
    }catch(exception &e){
        return false;
    }
}

void AppShareMemory::setCheck(bool check_overflow) {
    this->check_overflow = check_overflow;
}